/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2007 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    drvST7036SPI - Sitronix ST7036 Character LCD driver controller (SPI)   */
/*****************************************************************************/

#include "krnlTypes.h"
#include "drvIOConfig.h"
#include "sysCompiler.h"
#include "krnlSystemTimer.h"

///////////////////////////////////////////////////////////////////////////////
// Check definitions
#if lcdVOLTAGE != 3 && lcdVOLTAGE != 5
#error "lcdVOLTAGE: Lcd voltage is not defined."
#endif

///////////////////////////////////////////////////////////////////////////////
// Local functions
static void drvST7036WriteCommand( dosByte in_command );
static void drvST7036WriteData( dosByte in_data );

///////////////////////////////////////////////////////////////////////////////
// Local variables
#if lcdNUMBER_OF_LINES > 1
static dosROM_DATA( dosByte, l_line_address[4] ) = 
{
	0x00,
	0x40,
	0x14,
	0x54
};
#endif
static dosByte l_address = 0;

///////////////////////////////////////////////////////////////////////////////
// Initialize character LCD driver
void drvLCDInit(void)
{
	// init pins
	InitLCDSPI();
	InitLCDCSB();
	InitLCDRS();

	// power up delay
	krnlDelay(50);

	// Function Set
	#if lcdNUMBER_OF_LINES == 1
		drvST7036WriteCommand(0x31); // 8 bit data length, 1 line, instruction table 1
	#else
		drvST7036WriteCommand(0x39); // 8 bit data length, 2 lines, instruction table 1
	#endif
	krnlDelay(2);
	
	// Bias Set
	#if lcdNUMBER_OF_LINES == 1 || lcdNUMBER_OF_LINES == 2
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x14); // BS: 1/5, not 3 line LCD
		#else
			drvST7036WriteCommand(0x1c); // BS: 1/4, not 3 line LCD
		#endif
	#else
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x15); // BS: 1/5, 3 line LCD
		#else
			drvST7036WriteCommand(0x1d); // BS: 1/4, 3 line LCD
		#endif
	#endif
	
	// Power Control
	#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x55); // booster on, contrast C5, set C4
	#else
		#if lcdNUMBER_OF_LINES == 1
			drvST7036WriteCommand(0x51); // booster off, contrast C5, set C4
		#elif lcdNUMBER_OF_LINES == 2
			drvST7036WriteCommand(0x52); // booster off, contrast C5, set C4
		#else
			drvST7036WriteCommand(0x50); // booster off, contrast C5, set C4
		#endif
	#endif
	
	// Follower Control
	#if lcdNUMBER_OF_LINES == 1
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x6d);	// set voltage follower and gain
		#else
			drvST7036WriteCommand(0x6a);	// set voltage follower and gain
		#endif
	#elif lcdNUMBER_OF_LINES == 2
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x6d);	// set voltage follower and gain
		#else
			drvST7036WriteCommand(0x69);	// set voltage follower and gain
		#endif
	#else
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x6e); // set voltage follower and gain
		#else
			drvST7036WriteCommand(0x6c); // set voltage follower and gain
		#endif
	#endif
	
	// Contrast Set
	#if lcdNUMBER_OF_LINES == 1
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x7c);	// set contrast C3, C2, C1
		#else
			drvST7036WriteCommand(0x74);	// set contrast C3, C2, C1
		#endif
	#elif lcdNUMBER_OF_LINES == 2
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x78);	// set contrast C3, C2, C1
		#else
			drvST7036WriteCommand(0x74);	// set contrast C3, C2, C1
		#endif
	#else
		#if lcdVOLTAGE == 3
			drvST7036WriteCommand(0x72); // set contrast C3, C2, C1
		#else
			drvST7036WriteCommand(0x7c); // set contrast C3, C2, C1
		#endif
	#endif

	// Function Set
	#if lcdNUMBER_OF_LINES == 1
		drvST7036WriteCommand(0x30); // switch back to instruction table 0
	#else
		drvST7036WriteCommand(0x38); // switch back to instruction table 0
	#endif
	
	// Display ON/OFF
	drvST7036WriteCommand(0x0c); // display on, cursor off, cursor blink off

	// Clear Display
	drvST7036WriteCommand(0x01); // delete display, cursor at home
	krnlHighResDelay(2000);

	// Entry Mode Set
	drvST7036WriteCommand(0x06); // cursor auto-increment
	krnlHighResDelay(2000);
}

///////////////////////////////////////////////////////////////////////////////
// Cursor BLINK/ON/OFF
void drvLCDCursorMode( dosByte in_mode )
{
	drvST7036WriteCommand(0x0c + in_mode); // set cursor mode
}

///////////////////////////////////////////////////////////////////////////////
// Display Clear
void drvLCDClearDisplay(void)
{
	drvST7036WriteCommand(0x01); // delete display, cursor at home
	krnlHighResDelay(2000);
}

///////////////////////////////////////////////////////////////////////////////
// Moves cursor to the specified coordinates
dosBool drvLCDGotoXY( dosByte in_x, dosByte in_y )
{
	dosByte address = 0;
	dosByte success = dosTrue;
	
	// coordinates starts from 1
	in_y--;
	in_x--;

	// check if coordinates within range
	if( in_y < lcdNUMBER_OF_LINES && in_x < lcdNUMBER_OF_CHARACTERS )
	{
#if lcdNUMBER_OF_LINES > 1
		address = l_line_address[in_y] + in_x;
#else
		if( in_x < 8 )
			address = in_x;
		else
			address = in_x + 0x40 - 8;
#endif
	}
	else
		success = dosFalse;
	
	l_address = address;

	// send address
	drvST7036WriteCommand( 0x80 + address );
	
	return success;
}	

///////////////////////////////////////////////////////////////////////////////
// Write character
void drvLCDWriteCharacter( dosChar in_char )
{
	drvST7036WriteData( in_char );
	
	l_address++;

	// update address (wrap around) in the case of one line display
	#if lcdNUMBER_OF_LINES == 1
	if( l_address == 8 )
	{
		drvST7036WriteCommand( 0x80 | 0x40 );
	}
	#endif
}
 
///////////////////////////////////////////////////////////////////////////////
// Load custom characters
void drvLCDDowloadCustomCharacters( const dosByte* in_characters )
{
	dosByte i;
	
	// set CG RAM address
	drvST7036WriteCommand( 0x40 );
	
	for( i = 0; i < 64; i++ )
		drvST7036WriteData( in_characters[i] );
	
	// reset DD RAM address
	drvST7036WriteCommand( 0x80 );
}

///////////////////////////////////////////////////////////////////////////////
// download custom character
void drvLCDDowloadCustomCharacter( dosByte in_index, const dosByte* in_characters )
{
	dosByte i;
	
	// set CG RAM address
	drvST7036WriteCommand( 0x40 + 8 * in_index );
	
	for( i = 0; i < 8; i++ )
		drvST7036WriteData( in_characters[i] );
	
	// reset DD RAM address
	drvST7036WriteCommand( 0x80 );
}


///////////////////////////////////////////////////////////////////////////////
// Local functions
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Write command
static void drvST7036WriteCommand( dosByte in_command )
{
	// Command Mode, set RS low
	LCDRS(0);
	
	krnlHighResDelay(10);

	// chip select
	LCDCSB(0);
	
	// send command
	LCDSendReceiveByte( in_command );

	// remove chip selection
	LCDCSB(1);

	// Data Mode, set RS high
	LCDRS(1);
	
	krnlHighResDelay(50); // wait for display controller
}

///////////////////////////////////////////////////////////////////////////////
// Write data
static void drvST7036WriteData( dosByte in_data )
{
	// chip select
	LCDCSB(0);
	
	// send command
	LCDSendReceiveByte( in_data );

	// remove chip selection
	LCDCSB(1);

	krnlHighResDelay(50); // wait for display controller
}

